package com.dayuanit.dy15.atm.service.impl;

import com.dayuanit.dy15.atm.dto.CardDTO;
import com.dayuanit.dy15.atm.entity.Card;
import com.dayuanit.dy15.atm.entity.Flow;
import com.dayuanit.dy15.atm.enums.FlowTypeEnum;
import com.dayuanit.dy15.atm.exception.BizException;
import com.dayuanit.dy15.atm.mapper.CardMapper;
import com.dayuanit.dy15.atm.mapper.FlowMapper;
import com.dayuanit.dy15.atm.service.CardService;
import com.dayuanit.dy15.atm.util.CardUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Component
public class CardServiceImpl implements CardService {

    public CardServiceImpl() {
        System.out.println("CardServiceImpl()");
    }
    /**
     * 存款类型
     */
    public static final int DEPOSIT_FLOW_TYPE = 1;

    /**
     * 取款类型
     */
    public static final int DRAW_FLOW_TYPE = 2;

    @Autowired
    private CardMapper cardMapper;

    @Autowired
    private FlowMapper flowMapper;

    /**
     * 银行卡开户
     * @param password 密码
     * @param confirmPassword 确认密码
     */
    @Override
    public int openaccount(long userId, String password, String confirmPassword) {
        //TODO 参数校验 非空校验
        if (StringUtils.isBlank(password) || StringUtils.isBlank(confirmPassword)) {
            throw new BizException("有必填参数为空");
        }

        //TODO 长度校验 两次密码是否相等  强度的校验
        if (password.length() < 4 || confirmPassword.length() < 4) {
            throw new BizException("密码长度必须大于等于4");
        }

        if (!password.equals(confirmPassword)) {
            throw new BizException("两次密码不相等");
        }

        // 开户 向数据库添加一条记录
        //根据银行卡号查询一下是否重复
        String cardNum = CardUtil.createCardNum();
        Card card = cardMapper.getCardByNum(cardNum);
        if (null != card) {
            throw new BizException("卡号重复");
        }

        card = new Card();
        card.setBalance(0);
        card.setCardNum(cardNum);
        card.setCreateTime(new Date());
        card.setModifyTime(card.getCreateTime());
        card.setPassword(password);
        card.setStatus(1);
        //当前用户
        card.setUserId(userId);

        int rows = cardMapper.insert(card);
        if (1 != rows) {
            throw new BizException("开户失败");
        }
        return rows;

    }

    /**
     * 存款
     * spring开启事务，两种方式，1.xml配置，2.注解方式(推荐)
     *
     * 使用注解方式开启事务，注意加上事务注解的驱动。
     * 还要注意事务的失效问题，比如，service方法出现异常了，但是又将异常catch住后，将异常吃掉了，
     * 那么spring就认为方法正常执行了，所以就会commit事务。
     * 解决方案，在catch中再次将异常throw即可。
     *
     * 事务失效的第二种情况：
     *     默认情况下，事务注解只对非检查型异常有效果，对检查型异常不管不问。
     *     之所以这么做，我认为注解事务对检查型异常不做处理，是因为检查异常程序员自己会处理掉，
     *     如果spring对其再做处理，就显得多余。如果想让spring处理检查型异常，那么可以加一个
     *     属性rollbackFor=Exception.class
     *
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deposit(Long cardId, String password, String amount) throws Exception {
        //用户给的参数都是邪恶的
        if (null == cardId
                || StringUtils.isBlank(password)
                || StringUtils.isBlank(amount)) {
            throw new BizException("有必填参数为空");
        }

        Card card = cardMapper.selectByPrimaryKey(cardId);

        if (null == card) {
            throw new BizException("卡号不存在或者密码错误");
        }

        //校验密码
        if (!password.equals(card.getPassword())) {
            throw new BizException("卡号不存在或者密码错误");
        }

        //校验金额
        if (Double.parseDouble(amount) <= 0) {
            throw new BizException("存款金额必须大于零");
        }

        //加钱
        Integer balance = card.getBalance();
        double doubleAmount = Double.parseDouble(amount);
        balance = (int)(doubleAmount * 100) + balance;
        System.out.println("balance = " + balance);

        card.setBalance(balance);
        System.out.println(card.getBalance());
        int row = cardMapper.updateByPrimaryKey(card);
        if (1 != row) {
            throw new BizException("存款失败");
        }


        //非检查型异常
//        int i = 9/0;


//        if (1==1) {
//            //检查型异常
//            throw new Exception("出错啦...");
//        }

        // 添加流水
        Flow flow = new Flow();
        flow.setAmount((int)(Double.parseDouble(amount) * 100));
        flow.setCardId(card.getId());
        flow.setCreateTime(new Date());
        flow.setFlowType(FlowTypeEnum.DEPOSIT.getK());
        flow.setRemark(FlowTypeEnum.DEPOSIT.getV());
        flow.setUserId(card.getUserId());

        row = flowMapper.insert(flow);
        if (1 != row) {
            throw new BizException("存款失败");
        }

        return 0;
    }

    @Override
    public int withdraw(Long cardId, String password, String amount) {
        if (null == cardId
                || StringUtils.isBlank(password)
                || StringUtils.isBlank(amount)) {
            throw new BizException("有必填参数为空");
        }

        try {
            Card card = cardMapper.selectByPrimaryKey(cardId);

            if (null == card) {
                throw new BizException("卡号不存在或者密码错误");
            }

            //校验密码
            if (!password.equals(card.getPassword())) {
                throw new BizException("卡号不存在或者密码错误");
            }

            //校验金额
            if (Double.parseDouble(amount) <= 0) {
                throw new BizException("存款金额必须大于零");
            }

            //判断余额
            if (card.getBalance() < 100 * (int)Double.parseDouble(amount)) {
                throw new BizException("余额不足，请充值");
            }

            //减钱
            Integer balance = card.getBalance();
            double doubleAmount = Double.parseDouble(amount);
            balance =  balance - (int)(doubleAmount * 100);
            System.out.println("balance = " + balance);

            card.setBalance(balance);

            System.out.println(card.getBalance());

            int row = cardMapper.updateByPrimaryKey(card);
            if (1 != row) {
                throw new BizException("取款失败");
            }

            // 添加流水
            Flow flow = new Flow();
            flow.setAmount(-(int)(Double.parseDouble(amount) * 100));
            flow.setCardId(card.getId());
            flow.setCreateTime(new Date());
            flow.setFlowType(FlowTypeEnum.DRAW.getK());
            flow.setRemark(FlowTypeEnum.DRAW.getV());
            flow.setUserId(card.getUserId());

            row = flowMapper.insert(flow);
            if (1 != row) {
                throw new BizException("取款失败");
            }

            //提交事务
        } catch (Exception e) {
            //此处catch异常，是为了回滚事务，但是别忘记，要将异常上报给高层，告诉高层，有异常发生；否则servlet会做出错误的响应给用户。
            //回滚事务
            //将异常上抛，告诉高层，这里发生了异常
            throw e;
        }

        return 0;
    }

    /**
     * 转账
     * @param cardNumIn 转入银行卡
     * @param password 转出密码
     * @param amount 转账金额 单位：元
     * @return
     */
    @Override
    public int transfer(Long cardId, String cardNumIn, String password, String amount) {
        //TODO 参数校验
        if (null == cardId
                || StringUtils.isBlank(cardNumIn)
                || StringUtils.isBlank(password)
                || StringUtils.isBlank(amount)) {
            throw new BizException("有必填参数为空");
        }

        //TODO 获取sqlSession 开事务
        try {
            Card cardOut = cardMapper.selectByPrimaryKey(cardId);
            if (cardOut == null) {
                throw new BizException("转出卡号不存在或者密码错误");
            }

            Card cardIn = cardMapper.getCardByNum(cardNumIn);
            if (null == cardIn) {
                throw new BizException("转入卡号不存在");
            }

            //校验金额
            double doubleAmount = Double.parseDouble(amount);
            if (doubleAmount <= 0) {
                throw new BizException("转账金额必须大于零");
            }

            //校验密码
            if (!password.equals(cardOut.getPassword())) {
                throw new BizException("转出卡号不存在或者密码错误");
            }

            // 转出金额 流水
            int newBalance = cardOut.getBalance() - (int)(doubleAmount * 100);
            if (newBalance < 0) {
                throw new BizException("余额不足，请充值");
            }

            cardOut.setBalance(newBalance);

            int rows = cardMapper.updateByPrimaryKey(cardOut);
            if (1 != rows) {
                throw new BizException("转账失败");
            }

            Flow flow = new Flow();
            flow.setUserId(cardOut.getUserId());
            flow.setRemark(FlowTypeEnum.TRANSFER_OUT.getV());
            flow.setFlowType(FlowTypeEnum.TRANSFER_OUT.getK());
            flow.setCreateTime(new Date());
            flow.setCardId(cardOut.getId());
            flow.setAmount(-(int)(Double.parseDouble(amount) * 100));

            rows = flowMapper.insert(flow);
            if (1 != rows) {
                throw new BizException("转账失败");
            }

            // 转入金额 流水
            newBalance = cardIn.getBalance() + (int)(doubleAmount * 100);
            cardIn.setBalance(newBalance);

            rows = cardMapper.updateByPrimaryKey(cardIn);
            if (1 != rows) {
                throw new BizException("转账失败");
            }

            flow = new Flow();
            flow.setUserId(cardIn.getUserId());
            flow.setRemark(FlowTypeEnum.TRANSFER_IN.getV());
            flow.setFlowType(FlowTypeEnum.TRANSFER_IN.getK());
            flow.setCreateTime(new Date());
            flow.setCardId(cardIn.getId());
            flow.setAmount((int)(Double.parseDouble(amount) * 100));

            rows = flowMapper.insert(flow);
            if (1 != rows) {
                throw new BizException("转账失败");
            }

            // 提交事务或者
        } catch (Exception e) {
            throw e;
        }

        return 0;
    }

    @Override
    public List<CardDTO> queryCards(long userId) {

        List<Card> cards = cardMapper.listCard(userId);

        List<CardDTO> dtos = new ArrayList<>();
        for (Card card : cards) {
            CardDTO cardDTO = new CardDTO();
            dtos.add(cardDTO);

            cardDTO.setBalance(String.valueOf(card.getBalance()/ 100));
            cardDTO.setCardId(card.getId());
            cardDTO.setCardNum(CardUtil.formatNum(card.getCardNum()));
        }

        return dtos;
    }
}
